/*
 * @Author: Jimmy
 * @LastModifiedBy: Jimmy
 * @Date: 2024-05-26 14:50:26
 * @LastEditTime: 2024-08-23 22:10:57
 * @FilePath: /nest_study/server/src/common/guards/auth.guard.ts
 */
import { ExecutionContext, ForbiddenException, Inject, Injectable, LoggerService, UnauthorizedException, UseInterceptors } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Reflector } from "@nestjs/core";
import { AuthGuard } from "@nestjs/passport";
import { pathToRegexp } from "path-to-regexp";
import { UserService } from "src/modules/system/user/user.service";
import { ALLOW_ANON } from "../decorators/allow-anon.decorator";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
    private globalWhiteList = [];
    constructor(
        private readonly reflector: Reflector,
        @Inject(UserService)
        private readonly userService: UserService,
        private readonly config: ConfigService,
        @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: LoggerService
    ) {
        super();
        this.globalWhiteList = [].concat(this.config.get('perm.router.whitelist') || []);
    }

    async canActivate(ctx: ExecutionContext): Promise<boolean> {
        const isInWhiteList = this.checkWhiteList(ctx);
        if (isInWhiteList) { return true };
        const allowAnon = this.reflector.getAllAndOverride<boolean>(ALLOW_ANON, [ctx.getHandler(), ctx.getClass()]);
        if (allowAnon) return true;

        const req = ctx.switchToHttp().getRequest();
        const accessToken = req.get('Authorization');
        if (!accessToken) {
            throw new ForbiddenException('请重新登录');
        }
        const atUserId = await this.userService.parseToken(accessToken);
        if (!atUserId) {
            throw new UnauthorizedException('当前登录已过期，请重新登录')
        }
        return await this.activate(ctx);
    }

    async activate(ctx: ExecutionContext): Promise<boolean> {
        return super.canActivate(ctx) as Promise<boolean>;
    }

    checkWhiteList(ctx: ExecutionContext): boolean {
        const req = ctx.switchToHttp().getRequest();
        const i = this.globalWhiteList.findIndex((route) => {
            //请求方法类型相同
            if (req.method.toUpperCase() == route.method.toUpperCase()) {
                //对比Url
                return !!pathToRegexp(route.path).exec(req.url);
            }
            return false;
        })
        //在白名单中 则进行下一步，i===-1,则不在白名单中，需求对比是否有接口权限
        return i > -1;
    }
}